﻿using GanFeng.Forest.WebApi.Configs;
using GanFeng.Forest.WebApi.Exceptions;
using GanFeng.Forest.WebApi.Objects;
using hyjx.comm.Types;
using Hyjx.GanFengLogger;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System.Diagnostics;
using System.Text.Encodings.Web;
using System.Text.Json;

namespace GanFeng.Forest.WebApi
{
    /// <summary>
    /// 控制器基础
    /// </summary>
    public class ApiControllerBase<TController> : ControllerBase where TController : ControllerBase
    {
        readonly JsonSerializerOptions _jsonSerializerOptions;
        /// <summary>
        /// MES 访问成功
        /// </summary>
        protected string MesVisitSucceed { get; set; } = "MES访问成功,";
        /// <summary>
        /// MES 访问失败
        /// </summary>
        protected string MesVisitFailed { get; set; } = "MES访问失败,";

        /// <summary>
        /// 创建一个控制器基础
        /// </summary>
        /// <param name="logger">日志</param>
        /// <param name="config">配置</param>
        public ApiControllerBase(ILogger<TController> logger, IConfiguration config)
        {
            Logger = logger;
            Config = config;

            _jsonSerializerOptions = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
            SystemSetting = Config.GetSection("SystemSetting").Get<SystemSettingConfig>();
            Factory = Config.GetValue<int>("Factory");
            MaterialCodes = Config.GetSection("MaterialCode:MaterialCodes").Get<MaterialCodeConfig[]>();
            GlueMaterials = Config.GetSection("MaterialCode:Glue").Get<GlueMaterialConfig[]>();

            AppDataFolder = $"{Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)}\\hyjx";
        }
        /// <summary>
        /// 配置
        /// </summary>
        protected IConfiguration Config { get; }
        /// <summary>
        /// 日志
        /// </summary>
        protected ILogger<TController> Logger { get; }
        /// <summary>
        /// 工厂
        /// </summary>
        protected int Factory { get; }
        /// <summary>
        /// 物料编码集
        /// </summary>
        protected MaterialCodeConfig[]? MaterialCodes { get; }
        /// <summary>
        /// 制胶物料集
        /// </summary>
        protected GlueMaterialConfig[]? GlueMaterials { get; }

        /// <summary>
        /// 系统设置
        /// </summary>
        protected SystemSettingConfig? SystemSetting { get; }
        /// <summary>
        /// 最大工步
        /// </summary>
        protected int MaxStep { get; } = 24;
        /// <summary>
        /// 应用数据目录
        /// </summary>
        protected string AppDataFolder { get; }

        /// <summary>
        /// 转换物料编码
        /// </summary>
        /// <param name="materialCode">物料编码</param>
        /// <param name="plcFeatureType">设备类型</param>
        /// <param name="isGanFengMaterialCode">是否赣锋物料编码，false 表示要转换的是红运物料编码</param>
        /// <exception cref="ConvertMaterialCodeException"></exception>
        /// <returns>物料编码</returns>
        protected short ConvertMaterialCode(short materialCode, PlcFeatureTypes? plcFeatureType, bool isGanFengMaterialCode = true)
        {
            if (MaterialCodes == null)
                throw new ConvertMaterialCodeException(48);

            MaterialCodeConfig? material = null;
            if (isGanFengMaterialCode)
            {
                material = MaterialCodes.FirstOrDefault(p => p.GanFengValue == materialCode && p.FeatureType == plcFeatureType);
            }
            else
            {
                material = MaterialCodes.FirstOrDefault(p => p.HongYunValue == materialCode && p.FeatureType == plcFeatureType);
            }

            if (material == null)
                throw new ConvertMaterialCodeException(49);

            return isGanFengMaterialCode == true ? material.HongYunValue : material.GanFengValue;
        }

        /// <summary>
        /// 建立 OK 返回对象
        /// </summary>
        /// <param name="code">错误代码</param>
        /// <param name="message">信息</param>
        /// <param name="eventId">事件编号</param>
        /// <param name="log">日志对象</param>
        /// <param name="weight">重量</param>
        /// <param name="exception">异常</param>
        /// <returns></returns>
        protected OkObjectResult BuildWithWeightResult(int code, string message, double weight, LogObject log, int eventId = 1, Exception? exception = null)
        {
            OkObjectResult response = new(new WithWeightApiResult(code, weight));
            log.ResponseContent = JsonSerializer.Serialize(response, _jsonSerializerOptions);

            if (exception == null)
                LogMes(message, log);
            else
                LogMes(log, message, exception);

            return response;
        }

        /// <summary>
        /// 确认参数且替换物料编码
        /// </summary>
        /// <typeparam name="T">要确认的参数类型</typeparam>
        /// <param name="args">参数</param>
        /// <param name="featureType">参数</param>
        /// <exception cref="EnsureArgsAndCovertMaterialCodeException">参数错误</exception>
        protected void EnsureArgsAndCovertMaterialCode<T>(T args, PlcFeatureTypes? featureType) where T : class
        {
            if (args == null)
                throw new EnsureArgsAndCovertMaterialCodeException(1);
            // 下发配方
            if (args is MesRecipeArgs recipe)
            {
                if (recipe.Details == null)
                    throw new EnsureArgsAndCovertMaterialCodeException(1);
                if (string.IsNullOrEmpty(recipe.DeviceCode))
                    throw new EnsureArgsAndCovertMaterialCodeException(2);

                recipe.Details.Sort(new MesRecipeArgsDetailSerialNumberSortor()); // 给客户端传上来的工步按工步号排序
                foreach (var item in recipe.Details)
                {
                    try
                    {
                        item.MaterialCode = ConvertMaterialCode(item.MaterialCode, featureType);
                    }
                    catch (ConvertMaterialCodeException ex)
                    {
                        throw new EnsureArgsAndCovertMaterialCodeException(ex.Code);
                    }
                }
                return;
            }
            // 设置工步运行
            if (args is SetStepRunArgs stepRun)
            {
                if (string.IsNullOrEmpty(stepRun.DeviceCode))
                    throw new EnsureArgsAndCovertMaterialCodeException(2);

                return;
            }
            // 设置物料匹配
            if (args is SetMaterialMatchingArgs setMatching)
            {
                if (string.IsNullOrEmpty(setMatching.DeviceCode))
                    throw new EnsureArgsAndCovertMaterialCodeException(2);

                if (setMatching.StepNumber < 1 || setMatching.StepNumber > MaxStep)
                    throw new EnsureArgsAndCovertMaterialCodeException(18);

                try
                {
                    setMatching.MaterialCode = ConvertMaterialCode(setMatching.MaterialCode, featureType);
                }
                catch (ConvertMaterialCodeException ex)
                {
                    throw new EnsureArgsAndCovertMaterialCodeException(ex.Code);
                }

                return;
            }
            // 投料
            if (args is FeedingArgs feeding)
            {
                if (string.IsNullOrEmpty(feeding.DeviceCode))
                    throw new EnsureArgsAndCovertMaterialCodeException(2);

                try
                {
                    feeding.MaterialCode = ConvertMaterialCode(feeding.MaterialCode, featureType);
                }
                catch (ConvertMaterialCodeException ex)
                {
                    throw new EnsureArgsAndCovertMaterialCodeException(ex.Code);
                }
            }
            // 稀释搅拌设置工步运行
            if (args is DilutionSetStepRunArgs dRun)
            {
                if (string.IsNullOrEmpty(dRun.DeviceCode))
                    throw new EnsureArgsAndCovertMaterialCodeException(2);

                return;
            }
            // 稀释搅拌工步完成
            if (args is SetStepFinishArgs ssf)
            {
                if (string.IsNullOrEmpty(ssf.DeviceCode))
                    throw new EnsureArgsAndCovertMaterialCodeException(2);

                return;
            }
            // 稀释搅拌下发重量确认
            if (args is ConfirmWeightArgs confirm)
            {
                if (string.IsNullOrEmpty(confirm.DeviceCode))
                    throw new EnsureArgsAndCovertMaterialCodeException(2);

                return;
            }
        }
        /// <summary>
        /// 确认上料系统配置
        /// </summary>
        /// <exception cref="EnsureFeedingSystemSettingException">上料系统配置有误</exception>
        protected void EnsureFeedingSystemSetting()
        {
            if (SystemSetting == null || !SystemSetting.IsFeedingEnabled || SystemSetting.Feedings == null || !SystemSetting.Feedings.Any())
                throw new EnsureFeedingSystemSettingException(3);
        }

        /// <summary>
        /// 获取设备 PLC
        /// </summary>
        /// <param name="deviceCode">设备编码</param>
        /// <returns></returns>
        /// <exception cref="GetDevicePlcException">根据提供的设备编码，没找到对应的 PLC 配置</exception>
        protected PlcConfig GetDevicePlc(string deviceCode)
        {
            PlcConfig plc;

            // 先找搅拌系统
            if (SystemSetting == null ||
                !SystemSetting.IsMixerEnabled ||
                SystemSetting.Mixers == null ||
                !SystemSetting.Mixers.Any())
                throw new GetDevicePlcException(3);

            DevicePlcConfig? mixPlc = null;
            foreach (var pl in SystemSetting.Mixers.Where(p => p.IsEnabled))
            {
                if (pl.Plcs == null)
                    continue;

                mixPlc = pl.Plcs.FirstOrDefault(p => p.IsEnabled && p.ControlDevice == deviceCode);

                if (mixPlc != null)
                {
                    if (string.IsNullOrEmpty(mixPlc.Ip) || !hyjx.RegularExpressionHelper.IsIpAddress(mixPlc.Ip))
                        throw new GetDevicePlcException(5);

                    plc = new() { CpuType = mixPlc.CpuType, FeatureType = mixPlc.FeatureType, Ip = mixPlc.Ip, IsEnabled = mixPlc.IsEnabled, Rack = mixPlc.Rack, Slot = mixPlc.Slot, MesMode = mixPlc.MesMode };
                    return plc;
                }
            }

            // 搅拌系统找不到，找上料系统
            if (SystemSetting.IsFeedingEnabled && SystemSetting.Feedings != null)
            {
                foreach (var pl in SystemSetting.Feedings.Where(p => p.IsEnabled))
                {
                    if (pl.Tanks == null)
                        continue;

                    if (pl.Tanks.Any(p => p.DeviceCode == deviceCode))
                    {
                        if (string.IsNullOrEmpty(pl.Ip) || !hyjx.RegularExpressionHelper.IsIpAddress(pl.Ip))
                            throw new GetDevicePlcException(5);

                        plc = new() { CpuType = pl.CpuType, FeatureType = pl.FeatureType, Ip = pl.Ip, IsEnabled = pl.IsEnabled, Rack = pl.Rack, Slot = pl.Slot, MesMode = pl.MesMode };
                        return plc;
                    }
                }
            }

            // 上料系统找不到，找浆料输送系统
            if (SystemSetting.IsDeliveriesEnabled && SystemSetting.Deliveries != null)
            {
                foreach (var pl in SystemSetting.Deliveries.Where(p => p.IsEnabled))
                {
                    if (pl.ArtifactTanks == null)
                        continue;

                    if (pl.ArtifactTanks.Any(p => p.DeviceCode == deviceCode))
                    {
                        if (string.IsNullOrEmpty(pl.Ip) || !hyjx.RegularExpressionHelper.IsIpAddress(pl.Ip))
                            throw new GetDevicePlcException(5);

                        plc = new() { CpuType = pl.CpuType, FeatureType = pl.FeatureType, Ip = pl.Ip, IsEnabled = pl.IsEnabled, Rack = pl.Rack, Slot = pl.Slot, MesMode = pl.MesMode };
                        return plc;
                    }
                }
            }

            throw new GetDevicePlcException(4);
        }

        /// <summary>
        /// 记录日志并返回结果
        /// </summary>
        /// <param name="code">错误代码</param>
        /// <param name="log">日志</param>
        /// <returns>返回 http 200 及结果</returns>
        protected OkObjectResult BadResult(int code, LogObject log)
        {
            return BuildResult(code, MesVisitFailed, log, null);
        }
        /// <summary>
        /// 记录日志并返回结果
        /// </summary>
        /// <param name="log">日志</param>
        /// <returns>返回 http 200 及结果</returns>
        protected OkObjectResult OkResult(LogObject log)
        {
            return BuildResult(0, MesVisitSucceed, log, null);
        }
        /// <summary>
        /// 记录日志并返回结果
        /// </summary>
        /// <param name="ex">异常</param>
        /// <param name="code">错误码</param>
        /// <param name="log">日志</param>
        /// <returns>返回 http 200 及结果</returns>
        protected OkObjectResult ExceptionResult(int code, Exception? ex, LogObject log)
        {
            return BuildResult(code, "MES 访问出现异常。", log, ex);
        }

        private OkObjectResult BuildResult(int code, string message, LogObject log, Exception? exception)
        {
            OkObjectResult response = new(new ApiResult(code));
            log.ResponseContent = JsonSerializer.Serialize(response, _jsonSerializerOptions);

            if (exception == null)
                LogMes(message, log);
            else
                LogMes(log, message, exception);

            return response;
        }

        /// <summary>
        /// 初始化日志
        /// </summary>
        /// <param name="deviceCode">设备编码</param>
        /// <param name="interfaceId">接口 ID</param>
        /// <param name="args">参数</param>
        /// <returns>返回初始化的日志数据</returns>
        protected LogObject InitializeLog(string deviceCode, int interfaceId, object args)
        {
            LogObject log = new()
            {
                DeviceCode = deviceCode,
                InterfaceId = interfaceId,
                RequestUrl = Request.GetDisplayUrl(),
                RequestContent = JsonSerializer.Serialize(args, _jsonSerializerOptions)
            };
            return log;
        }

        /// <summary>
        /// 记录 MES 信息
        /// </summary>
        /// <param name="message">信息</param>
        /// <param name="data">数据</param>
        protected void LogMes(string message, LogObject data)
        {
            GFLogger logger = new(Logger, data.DeviceCode, data.InterfaceId, data.RequestUrl, data.RequestContent, data.ResponseContent);
            logger.Append(message);
            logger.CompleteWithInformation();
        }

        /// <summary>
        /// 记录 MES 异常
        /// </summary>
        /// <param name="data">数据</param>
        /// <param name="exception">异常</param>
        /// <param name="message">信息</param>
        protected void LogMes(LogObject data, string message, Exception exception)
        {
            GFLogger logger = new(Logger, data.DeviceCode, data.InterfaceId, data.RequestUrl, data.RequestContent, data.ResponseContent);
            logger.Append(message);
            logger.CompleteWithError(exception);
        }

        /// <summary>
        /// 执行上料系统定时任务
        /// </summary>
        /// <param name="argValue"></param>
        protected void ExecuteFeedingSystemTimedTask(string argValue)
        {
            try
            {
                string path = Path.Combine(AppContext.BaseDirectory, "FeedingSystemTimedTask", "FeedingSystemTimedTask.exe");
                using Process process = new();
                process.StartInfo.FileName = path;
                process.StartInfo.Arguments = argValue;
                //process.StartInfo.CreateNoWindow = true;
                process.StartInfo.UseShellExecute = false;
                //process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                process.Start();
            }
            catch (Exception ex)
            {
                GFLogger logger = new(Logger, "system", -1, string.Empty, string.Empty, string.Empty, 1);
                logger.Append(ex.Message);
                logger.CompleteWithError(ex);
            }
        }
        /// <summary>
        /// 获取上料系统的罐子
        /// </summary>
        /// <param name="deviceCode">设备编码</param>
        /// <returns></returns>
        /// <exception cref="GetFeedingTankException">获取上料罐错误</exception>
        protected TankConfig GetFeedingTank(string deviceCode)
        {
            if (SystemSetting == null || !SystemSetting.IsFeedingEnabled || SystemSetting.Feedings == null)
                throw new GetFeedingTankException(50);

            foreach (var pl in SystemSetting.Feedings.Where(p => p.IsEnabled))
            {
                if (pl.Tanks == null)
                    continue;

                var tank = pl.Tanks.FirstOrDefault(p => p.DeviceCode == deviceCode);

                return tank ?? throw new GetFeedingTankException(4);
            }

            throw new GetFeedingTankException(6);
        }

        /// <summary>
        /// 通过MES物料编码获取原材料储罐
        /// </summary>
        /// <param name="mesMaterialCode"></param>
        /// <returns></returns>
        /// <exception cref="GetFeedingTankException"></exception>
        protected TankConfig GetFeedingTankByMaterialCode(short mesMaterialCode)
        {
            if (SystemSetting == null || !SystemSetting.IsFeedingEnabled || SystemSetting.Feedings == null)
                throw new GetFeedingTankException(50);

            foreach (var pl in SystemSetting.Feedings.Where(p => p.IsEnabled))
            {
                if (pl.Tanks == null)
                    continue;

                var tank = pl.Tanks.FirstOrDefault(p => p.MesMaterialCode == mesMaterialCode);

                return tank ?? throw new GetFeedingTankException(4);
            }

            throw new GetFeedingTankException(6);
        }
    }
}
